package com.fudan.es.pack;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.ReverseNested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket;
import org.elasticsearch.search.aggregations.metrics.Avg;
import org.elasticsearch.search.aggregations.metrics.Sum;

import com.fudan.es.entity.FieldMeta;
import com.fudan.es.entity.Metric;

public class BucketAggsPack {

	protected String path;
	protected List<Metric> metricList;
	protected Map<String, AggregationBuilder> reverseAggs;

	protected void fetchRootMetric(Bucket bucket,Map<String,Object> bucketMap) {
		for(Metric metric : metricList) {
			FieldMeta metricField = metric.getField();
			if(metric.getField()==null) continue;
			if(!bucketMap.containsKey(metricField.getName())) {
				Map<String,Object> bucketPack = new HashMap<>();
				bucketPack.put("doc", new ArrayList<>());
				bucketMap.put(metricField.getName(), bucketPack);
			}
			if(path==null) {
				if(!metricField.getInNested()) {
					switch(metric.getType()) {
						case COUNT:{
							Terms subBucket = bucket.getAggregations().get(metric.getType()+"$"+metricField.getName());
							List<? extends Bucket> buckets = subBucket.getBuckets();
							Map<String,Object> pack =  (Map<String, Object>) bucketMap.get(metricField.getName());
							List<Map<String,Object>> list = (List<Map<String,Object>>) pack.get("doc");
							for (Bucket b : buckets) {
								Map<String,Object> bm = new HashMap<>();
								bm.put("field", b.getKey());
								bm.put("count", b.getDocCount());
								list.add(bm);
							}
							break;
						}
						case SUM:{
							Sum sum = bucket.getAggregations().get(metric.getType()+"$"+metricField.getName());
							Map<String,Object> map = (Map<String, Object>) bucketMap.get(metricField.getName());
							map.put(metric.getType().toString(), sum.getValue());
							break;
						}
						case AVG:{
							Avg avg = bucket.getAggregations().get(metric.getType()+"$"+metricField.getName());
							Map<String,Object> map = (Map<String, Object>) bucketMap.get(metricField.getName());
							map.put(metric.getType().toString(), avg.getValue());
							break;
						}
						default:{
							break;
						}
					}
				}else {
					switch(metric.getType()) {
					case COUNT:{
						Nested nested = bucket.getAggregations().get(metric.getType()+"$"+metricField.getNestedPath());
						Terms subBucket = nested.getAggregations().get(metric.getType()+"$"+metricField.getName());
						List<? extends Bucket> buckets = subBucket.getBuckets();
						Map<String,Object> pack =  (Map<String, Object>) bucketMap.get(metricField.getName());
						List<Map<String,Object>> list = (List<Map<String,Object>>) pack.get("doc");
						for (Bucket b : buckets) {
							Map<String,Object> bm = new HashMap<>();
							bm.put("field", b.getKey());
							bm.put("count", b.getDocCount());
							list.add(bm);
						}
						break;
					}
					case SUM:{
						Nested nested = bucket.getAggregations().get(metric.getType()+"$"+metricField.getNestedPath());
						Sum sum = nested.getAggregations().get(metric.getType()+"$"+metricField.getName());
						Map<String,Object> map = (Map<String, Object>) bucketMap.get(metricField.getName());
						map.put(metric.getType().toString(), sum.getValue());
						break;
					}
					case AVG:{
						Nested nested = bucket.getAggregations().get(metric.getType()+"$"+metricField.getNestedPath());
						Avg avg = nested.getAggregations().get(metric.getType()+"$"+metricField.getName());
						Map<String,Object> map = (Map<String, Object>) bucketMap.get(metricField.getName());
						map.put(metric.getType().toString(), avg.getValue());
						break;
					}
					default:{
						break;
					}
				}
				}
			}else {
				if(path.equals(metricField.getNestedPath())) {
					switch(metric.getType()) {
					case COUNT:{
						Terms subBucket = bucket.getAggregations().get(metric.getType()+"$"+metricField.getName());
						List<? extends Bucket> buckets = subBucket.getBuckets();
						Map<String,Object> pack =  (Map<String, Object>) bucketMap.get(metricField.getName());
						List<Map<String,Object>> list = (List<Map<String,Object>>) pack.get("doc");
						for (Bucket b : buckets) {
							Map<String,Object> bm = new HashMap<>();
							bm.put("field", b.getKey());
							bm.put("count", b.getDocCount());
							list.add(bm);
						}
						break;
					}
					case SUM:{
						Sum sum = bucket.getAggregations().get(metric.getType()+"$"+metricField.getName());
						Map<String,Object> map = (Map<String, Object>) bucketMap.get(metricField.getName());
						map.put(metric.getType().toString(), sum.getValue());
						break;
					}
					case AVG:{
						Avg avg = bucket.getAggregations().get(metric.getType()+"$"+metricField.getName());
						Map<String,Object> map = (Map<String, Object>) bucketMap.get(metricField.getName());
						map.put(metric.getType().toString(), avg.getValue());
						break;
					}
					default:{
						break;
					}
				}
				}
			}
		}
	}

	protected void fetchReverseNestedMetric(ReverseNested reverseNested,Map<String,Object> reverseNestedMap) {
		String reversePath = reverseNested.getName();
		for (Metric metric : metricList) {
			FieldMeta metricField = metric.getField();
			if(metricField == null) continue;
			if(!reverseNestedMap.containsKey(metricField.getName())) {
				Map<String,Object> bucketPack = new HashMap<>();
				bucketPack.put("doc", new ArrayList<>());
				reverseNestedMap.put(metricField.getName(), bucketPack);
			}
			if(metricField.getInNested()) {
				String metricPath = metricField.getNestedPath();
				if(metricPath.equals(path)) continue;
				if(reverseAggs.containsKey(metricPath)) {
					if(reversePath.equals(metricPath)) {
						switch(metric.getType()) {
						case COUNT:{
							Terms terms = reverseNested.getAggregations().get(metric.getType()+"$"+metricField.getName());
							List<? extends Bucket> buckets = terms.getBuckets();
							Map<String,Object> pack =  (Map<String, Object>) reverseNestedMap.get(metricField.getName());
							List<Map<String,Object>> list = (List<Map<String,Object>>) pack.get("doc");
							for (Bucket bucket : buckets) {
								Map<String,Object> map = new HashMap<>();
								map.put("field", bucket.getKey());
								map.put("count", bucket.getDocCount());
								list.add(map);
							}
							break;
						}
						case SUM:{
							Sum sum = reverseNested.getAggregations().get(metric.getType()+"$"+metricField.getName());
							Map<String,Object> map = (Map<String, Object>) reverseNestedMap.get(metricField.getName());
							map.put(metric.getType().toString(), sum.getValue());
							break;
						}
						case AVG:{
							Avg avg = reverseNested.getAggregations().get(metric.getType()+"$"+metricField.getName());
							Map<String,Object> map = (Map<String, Object>) reverseNestedMap.get(metricField.getName());
							map.put(metric.getType().toString(), avg.getValue());
							break;
						}
						}
					}
				}else {
					if("root".equals(reversePath)) {
						switch(metric.getType()) {
						case COUNT:{
							Nested nested = reverseNested.getAggregations().get(metric.getType()+"$"+metricPath);
							Terms terms = nested.getAggregations().get(metric.getType()+"$"+metricField.getName());
							List<? extends Bucket> buckets = terms.getBuckets();
							Map<String,Object> pack =  (Map<String, Object>) reverseNestedMap.get(metricField.getName());
							List<Map<String,Object>> list = (List<Map<String,Object>>) pack.get("doc");
							for (Bucket bucket : buckets) {
								Map<String,Object> map = new HashMap<>();
								map.put("field", bucket.getKey());
								map.put("count", bucket.getDocCount());
								list.add(map);
							}
							break;
						}
						case SUM:{
							Nested nested = reverseNested.getAggregations().get(metric.getType()+"$"+metricPath);
							Sum sum = nested.getAggregations().get(metric.getType()+"$"+metricField.getName());
							Map<String,Object> map = (Map<String, Object>) reverseNestedMap.get(metricField.getName());
							map.put(metric.getType().toString(), sum.getValue());
							break;
						}
						case AVG:{
							Nested nested = reverseNested.getAggregations().get(metric.getType()+"$"+metricPath);
							Avg avg = nested.getAggregations().get(metric.getType()+"$"+metricField.getName());
							Map<String,Object> map = (Map<String, Object>) reverseNestedMap.get(metricField.getName());
							map.put(metric.getType().toString(), avg.getValue());
							break;
						}
						}

					}
				}
			}else {
				if("root".equals(reversePath)) {
					switch(metric.getType()) {
					case COUNT:{
						Terms terms = reverseNested.getAggregations().get(metric.getType()+"$"+metricField.getName());
						List<? extends Bucket> buckets = terms.getBuckets();
						Map<String,Object> pack =  (Map<String, Object>) reverseNestedMap.get(metricField.getName());
						List<Map<String,Object>> list = (List<Map<String,Object>>) pack.get("doc");
						for (Bucket bucket : buckets) {
							Map<String,Object> map = new HashMap<>();
							map.put("field", bucket.getKey());
							map.put("count", bucket.getDocCount());
							list.add(map);
						}
						break;
					}
					case SUM:{
						Sum sum = reverseNested.getAggregations().get(metric.getType()+"$"+metricField.getName());
						Map<String,Object> map = (Map<String, Object>) reverseNestedMap.get(metricField.getName());
						map.put(metric.getType().toString(), sum.getValue());
						break;
					}
					case AVG:{
						Avg avg = reverseNested.getAggregations().get(metric.getType()+"$"+metricField.getName());
						Map<String,Object> map = (Map<String, Object>) reverseNestedMap.get(metricField.getName());
						map.put(metric.getType().toString(), avg.getValue());
						break;
					}
					}

				}
			}
		}
	}

	public List<Metric> getMetricList() {
		return metricList;
	}

	public void setMetricList(List<Metric> metricList) {
		this.metricList = metricList;
	}
}
